package graphics;

import entities.exceptions.NotEnoughSuppliesToMakeRobotException;
import entities.exceptions.NotEnoughSupplyException;
import entities.exceptions.WrongFactoryException;
import java.util.Iterator;
import java.util.ResourceBundle;
import java.util.HashMap;
import java.awt.event.MouseEvent;
import javax.swing.JButton;
import java.awt.event.MouseListener;
import java.util.ArrayList;
import javax.swing.JLabel;
import java.util.MissingResourceException;
import entities.*;
import entities.exceptions.BlockedTileException;
import entities.exceptions.MissingRouteException;
import entities.exceptions.RouteAlreadyExistsException;
import entities.exceptions.WrongSupplyException;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.FlowLayout;
import java.awt.Graphics;
import java.awt.GridLayout;
import java.awt.Image;
import java.awt.Point;
import javax.swing.BoxLayout;
import javax.swing.JPanel;
import javax.swing.border.LineBorder;
import javax.swing.border.TitledBorder;
import willispace.WilliSpace;

import static entities.States.*;

/**
 * Titles of actions buttons used in entity window
 * @author Mateusz
 */
enum buttonsTitles {
    
    ADD_ROUTE_TO_SUPPLY, ADD_ROUTE_TO_FACTORY, DESTROY_FACTORY, DESTROY_ROBOT, GO, BUILD_GAS_FACTORY, BUILD_DIAMOND_FACTORY, MAKE_WORKER, MAKE_TRANSPORTER, CHANGE_SUPPLY
}

/**
 * Entity Window Class
 * Defines look and events for this window, shows specific entity properties and it actions
 * @author Mateusz
 */
public class EntityWindow extends CustomWindow implements MouseListener {

    /**
     * Messages to use in message panel
     */
    enum message {
        
        NONE, WRONG_TILE, BLOCKED_TILE, MISSING_ROUTE, MISSING_ROUTE_TO_FACTORY_OR_SUPPLY, WRONG_SUPPLY, ROUTE_ALREADY_EXISTS, NOT_ENOUGH_SUPPLIES_TO_MAKE_ROBOT, NOT_ENOUGH_SUPPLIES_TO_MAKE_FACTORY, WRONG_FACTORY,
        WAIT_FOR_EMPTY_TILE, WAIT_FOR_FACTORY, WAIT_FOR_SUPPLY
    }
    /**
     * item of topPanel, containing image of entity
     */
    public JPanel imagePanel;
    /**
     * one of main panels
     * contains properties of entity
     */
    public JPanel propertiesPanel;
    /**
     * one of main panels
     * contains actions (buttons) of entity
     */
    public JPanel actionsPanel;
    /**
     * item of topPanel
     * shows messages after action (click)
     */
    public JPanel messagePanel;
    /**
     * one of main panels
     * contains imagePanel and messagePanel
     */
    public JPanel topPanel;
    /**
     * inside panel of propertiesPanel
     */
    public PropertiesPanel insidePropertiesPanel;
    /**
     * entity, which is displayed
     */
    protected Entity entityClicked = null;
    /**
     * list of all buttons that might be used in actions
     */
    protected HashMap<buttonsTitles, ActionButton> buttonsList;

    /**
     * Class constructor, specifying size and position of window
     * @param name      name of entity window
     * @param size      size of entity window
     * @param position  position of entity window
     */
    public EntityWindow(String name, Dimension size, Point position) {
        this(name, size.width, size.height, position.x, position.y);
    }

    /**
     * Class constructor, specifying size and position of window
     * @param name      name of entity window
     * @param width     width of entity window
     * @param height    height of entity window
     * @param posX      position X (horizontal) of entity window
     * @param posY      position Y (vertical) of entity window
     */
    public EntityWindow(String name, int width, int height, int posX,
            int posY) {
        
        super(name, width, height, posX, posY);     // call parent constructor
        setAlwaysOnTop(true);                       // keep this window always on top

        // initialization
        buttonsList = new HashMap<buttonsTitles, ActionButton>();
       
        
        // call proper properties file
        rb = java.util.ResourceBundle.getBundle("graphics/EntityWindow");

        // set main panel
        addMainPanel();

        // set layout of main panel
        mainPanel.setLayout(new GridLayout(3, 1));
        
        // add 3 new panels to mainPanel

        // image panel
        imagePanel = new JPanel();
        imagePanel.setLayout(new FlowLayout(FlowLayout.CENTER));

        // message panel
        messagePanel = new JPanel();
        
        topPanel = new JPanel();
        topPanel.setLayout(new BoxLayout(topPanel, BoxLayout.Y_AXIS));
        topPanel.add(imagePanel);
        topPanel.add(messagePanel);
        
        mainPanel.add(topPanel);

        // properties panel
        propertiesPanel = new JPanel();
        propertiesPanel.setBorder(new TitledBorder(new LineBorder(
                Color.black), rb.getString("PROPERTIES")));
        propertiesPanel.setLayout(new GridLayout(1, 0));
        insidePropertiesPanel = new PropertiesPanel(null);
        propertiesPanel.add(insidePropertiesPanel);
        
        mainPanel.add(propertiesPanel);

        // actions panel
        actionsPanel = new JPanel();
        actionsPanel.setBorder(new TitledBorder(new LineBorder(
                Color.black), rb.getString("ACTIONS")));
        actionsPanel.setLayout(new BoxLayout(actionsPanel, BoxLayout.Y_AXIS));

        // add buttons to actions panel
        for (buttonsTitles buttonName : buttonsTitles.values()) {
            ActionButton ab = new ActionButton(buttonName, this);
            buttonsList.put(buttonName, ab);
            actionsPanel.add(ab);
        }
        
        hideAllActionsButtons();
        
        mainPanel.add(actionsPanel);
    }

    /**
     * Method called after some entity (or Tile) was clicked
     * Defines states and / or actions to make, depending on previous clicking
     * @param mp    panel (entity, tile) that was clicked
     */
    public void call(MyPanel mp) {
        
        message msg = message.NONE;
        boolean changeEntity = false;
        boolean resetStates = true;


        // if tile was clicked
        if (mp instanceof Tile) {
            if (entityClicked != null) {
                // if actual entity wasn't waiting for someting
                if (entityClicked.waitFor == WaitingStates.NONE) {
                    // show global stats
                    changeEntity = true;
                } // entity waites for tile - have to be builder
                else if (entityClicked.waitFor == WaitingStates.TILE) {
                    try {
                        if (entityClicked.state == BuilderStates.READY_TO_BUILD_DIAMOND_FACTORY) {
                            WilliSpace.buildDiamondFactory((Tile) mp);
                        } else if (entityClicked.state == BuilderStates.READY_TO_BUILD_GAS_FACTORY) {
                            WilliSpace.buildGasFactory((Tile) mp);
                        } else if (entityClicked.state == BuilderStates.READY_TO_GO) {
                            WilliSpace.moveBuilder((Builder) entityClicked, (Tile) mp);
                        }
                        
                    } catch (BlockedTileException e) {
                        msg = message.BLOCKED_TILE;
                    } catch (NotEnoughSupplyException ex) {
                        msg = message.NOT_ENOUGH_SUPPLIES_TO_MAKE_FACTORY;
                    }
                } // waiting for sth else
                else {
                    msg = message.WRONG_TILE;
                }
            }
        } // entity was clicked 
        else {
            Entity e = (Entity) mp;
            Point tileXY = e.getXY();


            // if global stats were displayed
            if (entityClicked == null) {
                changeEntity = true;
            } // if some entity was displayed
            else {
                // wasn't waiting for anything - just show this entity
                if (entityClicked.waitFor == WaitingStates.NONE) {
                    changeEntity = true;
                }

                // ==============================
                // BUILDER
                // ============================== 
                if (e instanceof Builder) {
                    // noone waits for builder - wrong tile clicked
                    if (entityClicked.waitFor != WaitingStates.NONE) {
                        msg = message.WRONG_TILE;
                        resetStates = true;
                    }
                } // ==============================
                // SUPPLY
                // ==============================
                else if (e instanceof Supply) {
                    // waited for supply
                    if (entityClicked.waitFor == WaitingStates.SUPPLY) {
                        // worker was waiting for supply
                        if (entityClicked instanceof Worker) {
                            // worker was waiting for route change
                            try {
                                WilliSpace.changeWorkerRoute((Worker) entityClicked, (Supply) e);
                            } // route for this supply is not added to factory 
                            catch (MissingRouteException ex) {
                                msg = message.MISSING_ROUTE;
                            } // wrong supply was clicked
                            catch (WrongSupplyException ex) {
                                msg = message.WRONG_SUPPLY;
                            }
                            resetStates = true;
                        } // factory is waiting for new route to supply
                        else if (entityClicked instanceof Factory) {
                            try {
                                WilliSpace.addRouteFromFactoryToSupply((Factory) entityClicked, (Supply) e);                                
                            } // wrong supply was clicked 
                            catch (WrongSupplyException ex) {
                                msg = message.WRONG_SUPPLY;
                            } // route already exists 
                            catch (RouteAlreadyExistsException ex) {
                                msg = message.ROUTE_ALREADY_EXISTS;
                            }
                            resetStates = true;
                        }
                        
                    } // wasn't waiting for anything
                    else if (entityClicked.waitFor != WaitingStates.NONE) {
                        msg = message.WRONG_TILE;
                        resetStates = true;
                    }
                } // ==============================
                // WORKER || TRANSPORTER
                // ==============================
                else if (e instanceof Worker
                        || e instanceof Transporter) {
                    // waiting for sth else - must be mistake
                    if (entityClicked.waitFor != WaitingStates.NONE) {
                        msg = message.WRONG_TILE;
                        resetStates = true;
                    }
                } // ==============================
                // FACTORY
                // ==============================
                else if (e instanceof Factory) {
                    // waited for factory
                    if (entityClicked.waitFor == WaitingStates.FACTORY) {
                        // transporter - change next target
                        if (entityClicked instanceof Transporter) {
                            try {
                                WilliSpace.addTransporterTarget((Transporter) entityClicked, (Factory) e);
                            } // route does not exists
                            catch (MissingRouteException ex) {
                                msg = message.MISSING_ROUTE;
                            }
                        } // factory - add new route to another factory
                        else if (entityClicked instanceof Factory) {
                            try {
                                WilliSpace.addRouteFromFactoryToFactory((Factory) entityClicked, (Factory) e);
                            } // route already exists
                            catch (RouteAlreadyExistsException ex) {
                                msg = message.ROUTE_ALREADY_EXISTS;
                            } // tried to add route from factory to itself 
                            catch (WrongFactoryException ex) {
                                msg = message.WRONG_FACTORY;
                            }
                            resetStates = true;
                        }
                    } // czekano na coś innego
                    else if (entityClicked.waitFor != WaitingStates.NONE) {
                        msg = message.WRONG_TILE;
                        //resetStates = true;
                    }
                }
            }
        }

        // reset state of entityClicked
        resetStates();

        // change entity for new
        if (changeEntity) {
            if (mp instanceof Tile) {
                entityClicked = null;
            } else {
                entityClicked = (Entity) mp;
            }
            changeEntity(entityClicked);
        }
        
        addMessage(msg, Color.red);
    }
    
    private void hideAllActionsButtons() {
        for (ActionButton ab : buttonsList.values()) {
            ab.setVisible(false);
        }
    }

    /**
     * Add message to new JLabel, displayed in messagePanel
     * @param msg   message to display
     * @param c     color of the message
     */
    protected void addMessage(message msg, Color c) {
        String text = msg.toString();
        messagePanel.removeAll();
        if (msg != message.NONE) {
            try {
                text = rb.getString(msg.toString());
            } catch (MissingResourceException ex) {
            }
            JLabel jl = new JLabel(text);
            jl.setForeground(c);
            messagePanel.add(jl);
        }
    }

    // resets entity stated
    private void resetStates() {
        if (entityClicked != null) {
            entityClicked.waitFor = WaitingStates.NONE;
            entityClicked.state = entities.States.EntityStates.NONE;
        }
    }
    
    private void changeEntity(Entity e) {
        hideAllActionsButtons();
        
        if (e != null) {
            changeImage(e);
            
            showButtons(e);
            
        } else {
            removeImage();
        }
        
        changeProperties(e);
    }

    /**
     * Show proper buttons for entity
     * @param e current entity
     */
    private void showButtons(Entity e) {
        ArrayList<buttonsTitles> ar = new ArrayList<buttonsTitles>();

        // show proper buttons
        if (e instanceof Transporter) {
            ar.add(buttonsTitles.DESTROY_ROBOT);
            ar.add(buttonsTitles.ADD_ROUTE_TO_FACTORY);
        } else if (e instanceof Worker) {
            ar.add(buttonsTitles.DESTROY_ROBOT);
            ar.add(buttonsTitles.CHANGE_SUPPLY);
        } else if (e instanceof Factory) {
            ar.add(buttonsTitles.DESTROY_FACTORY);
            ar.add(buttonsTitles.MAKE_TRANSPORTER);
            ar.add(buttonsTitles.MAKE_WORKER);
            ar.add(buttonsTitles.ADD_ROUTE_TO_FACTORY);
            ar.add(buttonsTitles.ADD_ROUTE_TO_SUPPLY);
        } else if (e instanceof Builder) {
            ar.add(buttonsTitles.GO);
            ar.add(buttonsTitles.BUILD_DIAMOND_FACTORY);
            ar.add(buttonsTitles.BUILD_GAS_FACTORY);
        }
        
        Iterator it = ar.iterator();
        
        while (it.hasNext()) {
            buttonsList.get((buttonsTitles) it.next()).setVisible(true);
        }
    }
    
    private void removeImage() {
        imagePanel.removeAll();
    }
    
    private void changeImage(Entity e) {
        removeImage();
        imagePanel.add(new ImagePanel(e.getDefaultImage()));
        //imagePanel.validate();
    }
    
    private void changeProperties(MyPanel mp) {
        propertiesPanel.removeAll();
        
        if (mp instanceof Entity) {
            insidePropertiesPanel = new PropertiesPanel(((Entity) mp).getProperties());
        } else {
            insidePropertiesPanel = new PropertiesPanel(null);
        }
        
        propertiesPanel.add(insidePropertiesPanel);
        propertiesPanel.validate();
    }
    
    @Override
    public void mouseReleased(final MouseEvent e) {
        if (WilliSpace.isPaused()) {
            return;
        }
        
        java.awt.EventQueue.invokeLater(new Runnable() {
            
            @Override
            public void run() {
                message msg = message.NONE;
                ActionButton ab = (ActionButton) e.getSource();
                buttonsTitles title = ab.getTitle();
                boolean goodMsg = true;
                
                switch (title) {
                    case ADD_ROUTE_TO_SUPPLY:
                        entityClicked.state = FactoryStates.WAIT_FOR_TARGET;
                        entityClicked.waitFor = WaitingStates.SUPPLY;
                        msg = message.WAIT_FOR_SUPPLY;
                        break;
                    
                    case ADD_ROUTE_TO_FACTORY:
                        entityClicked.state = FactoryStates.WAIT_FOR_TARGET;
                        entityClicked.waitFor = WaitingStates.FACTORY;
                        msg = message.WAIT_FOR_FACTORY;
                        break;
                    
                    case BUILD_DIAMOND_FACTORY:
                        // kliknięty builder, czeka na tile
                        msg = message.WAIT_FOR_EMPTY_TILE;
                        entityClicked.state = BuilderStates.READY_TO_BUILD_DIAMOND_FACTORY;
                        entityClicked.waitFor = WaitingStates.TILE;
                        break;
                    
                    case BUILD_GAS_FACTORY:
                        msg = message.WAIT_FOR_EMPTY_TILE;
                        entityClicked.state = BuilderStates.READY_TO_BUILD_GAS_FACTORY;
                        entityClicked.waitFor = WaitingStates.TILE;
                        break;
                    
                    case CHANGE_SUPPLY:
                        entityClicked.state = WorkerStates.READY_TO_CHANGE_SUPPLY;
                        entityClicked.waitFor = WaitingStates.SUPPLY;
                        break;
                    
                    case DESTROY_ROBOT:
                        WilliSpace.destroyRobot((Robot) entityClicked);
                        entityClicked = null;
                        changeEntity(entityClicked);
                        break;
                    
                    case DESTROY_FACTORY:
                        WilliSpace.destroyFactory((Factory) entityClicked);
                        entityClicked = null;
                        changeEntity(entityClicked);
                        break;
                    
                    case GO:
                        msg = message.WAIT_FOR_EMPTY_TILE;
                        entityClicked.state = BuilderStates.READY_TO_GO;
                        entityClicked.waitFor = WaitingStates.TILE;
                        break;
                    
                    case MAKE_TRANSPORTER:
                        try {
                            goodMsg = false;
                            WilliSpace.factoryMakeTransporter((Factory) entityClicked);
                            goodMsg = true;
                        } catch (MissingRouteException ex) {
                            msg = message.MISSING_ROUTE_TO_FACTORY_OR_SUPPLY;
                        } catch (NotEnoughSuppliesToMakeRobotException ex) {
                            msg = message.NOT_ENOUGH_SUPPLIES_TO_MAKE_ROBOT;
                        }
                        break;
                    
                    case MAKE_WORKER:
                        try {
                            goodMsg = false;
                            WilliSpace.factoryMakeWorker((Factory) entityClicked);
                            goodMsg = true;
                        } catch (MissingRouteException ex) {
                            msg = message.MISSING_ROUTE_TO_FACTORY_OR_SUPPLY;
                        } catch (NotEnoughSuppliesToMakeRobotException ex) {
                            msg = message.NOT_ENOUGH_SUPPLIES_TO_MAKE_ROBOT;
                        }
                        break;
                }
                
                Color msgColor;
                if (!goodMsg) {
                    msgColor = Color.red;
                } else {
                    msgColor = Color.green;
                }
                addMessage(msg, msgColor);
            }
        });
    }
    
    @Override
    public void mousePressed(MouseEvent e) {
    }
    
    @Override
    public void mouseClicked(MouseEvent e) {
    }
    
    @Override
    public void mouseEntered(MouseEvent e) {
    }
    
    @Override
    public void mouseExited(MouseEvent e) {
    }
}

/**
 * Class used in imagePanel, drawing an image as panel background
 * @author Mateusz
 */
class ImagePanel extends JPanel {
    
    protected Image img;
    
    public ImagePanel(Image img) {
        this.img = img;
        
        Dimension size = new Dimension(img.getWidth(null), img.getHeight(null));
        setLayout(null);
        setPreferredSize(size);
        setMinimumSize(size);
        setMaximumSize(size);
        setSize(size);
        setOpaque(false);
    }
    
    public Image getImage() {
        return img;
    }
    
    @Override
    public void update(Graphics g) {
        paintComponent(g);
    }
    
    @Override
    public void paintComponent(Graphics g) {
        super.paintComponent(g);
        g.drawImage(img, 0, 0, null);
    }
}

/**
 * Action button
 * Automatically adds mouse listener to entity window
 * @author Mateusz
 */
class ActionButton extends JButton {
    
    ResourceBundle rb = java.util.ResourceBundle.getBundle("graphics/ActionButton");
    buttonsTitles bt;
    
    public ActionButton(buttonsTitles bt, EntityWindow ew) {
        this.bt = bt;
        String txt = bt.toString();
        try {
            txt = rb.getString(txt);
        } catch (MissingResourceException ex) {
        }
        
        setText(txt);
        addMouseListener(ew);
    }
    
    public buttonsTitles getTitle() {
        return bt;
    }
}
